﻿using MongoDB.Driver;
using MongoDB.Driver.Linq;
using Sustainalytics.DataLayer.Exceptions.AccountsAndUsers;
using Sustainalytics.DataLayer.Shared;
using Sustainalytics.Entities;
using Sustainalytics.Utils;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Sustainalytics.DataLayer
{
    public class AccountsRepository : IAccountsRepository
    {
        private readonly MongoClient _client;
        private readonly IMongoDatabase _clientMongoDb;
        private readonly string _databaseName;

        private readonly DateTime _crmMinDate = new DateTime(1900, 1, 1);
        private readonly IMongoCollection<Account> _accountCollection;

        public static IAccountsRepository GetAccountsRepository(string connectionString)
        {
            return new AccountsRepository(connectionString);
        }

        private AccountsRepository(string connectionString)
        {
            var dbConnectionString = MongoUrl.Create(connectionString);

            if (string.IsNullOrWhiteSpace(dbConnectionString.DatabaseName))
            {
                throw new ArgumentException("No database specified in the connection string!");
            }

            _databaseName = dbConnectionString.DatabaseName;
            _client = new MongoClient(dbConnectionString);
            _clientMongoDb = _client.GetDatabase(dbConnectionString.DatabaseName);

            _accountCollection = MongoDatabaseFactory.GetDatabase(connectionString).GetCollection<Account>();
        }

        public void Cleanup()
        {
            _clientMongoDb.Client.DropDatabase(_databaseName);
        }

        public List<Account> GetAccountsIncludingNameOnly(Status status, bool hasPlatformAccess)
        {
            return _clientMongoDb.GetCollection<Account>(typeof(Account).Name)
                .Find(Builders<Account>.Filter.And(
                    Builders<Account>.Filter.Eq(a => a.Status, status),
                    Builders<Account>.Filter.Eq(a => a.HasPlatformAccess, hasPlatformAccess)))
                .Project<Account>(Builders<Account>.Projection.Include(a => a.Name))
                .ToList();
        }

        public List<Account> GetAccounts(Status? status, string searchText, ListOptions listOptions, bool tier1_or_tier2 = false)
        {
            var coll = _clientMongoDb.GetCollection<Account>(typeof(Account).Name);
            var q = coll.AsQueryable();
            if (status != null)
            {
                q = q.Where(a => a.Status == status.Value);
            }
            if (!string.IsNullOrWhiteSpace(searchText))
            {
                q = q.Where(a => a.Name.ToUpperInvariant().Contains(searchText.ToUpperInvariant()));
            }

            if (tier1_or_tier2)
            {
                q = q.Where(a => a.PlatformTier == AccessLevel.Tier1 || a.PlatformTier == AccessLevel.Tier2);
            }

            if (listOptions != null && listOptions.OrderBy != null && listOptions.OrderBy.Any())
            {
                foreach (var sortOption in listOptions.OrderBy)
                {
                    var accountsSortBy = (AccountsSortEnum)Enum.Parse(typeof(AccountsSortEnum), sortOption, true);

                    switch (accountsSortBy)
                    {
                        case AccountsSortEnum.Name:
                            q = listOptions.OrderAscending ? q.OrderBy(a => a.Name) : q.OrderByDescending(a => a.Name);
                            break;

                        case AccountsSortEnum.RelationshipType:
                            q = listOptions.OrderAscending ? q.OrderBy(a => a.RelationshipType) : q.OrderByDescending(a => a.RelationshipType);
                            break;

                        case AccountsSortEnum.AccountManagerName:
                            q = listOptions.OrderAscending ? q.OrderBy(a => a.AccountManager.FullName) : q.OrderByDescending(a => a.AccountManager.FullName);
                            break;

                        case AccountsSortEnum.Status:
                            q = listOptions.OrderAscending ? q.OrderBy(a => a.Status) : q.OrderByDescending(a => a.Status);
                            break;

                        default:
                            break;
                    }
                }
            }

            if (listOptions == null)
                return null;

            return q.Skip(listOptions.Skip).Take(listOptions.Take).ToList();
        }

        public int GetAccountsTotalCount(Status? status, string searchText, bool tier1_or_tier2 = false)
        {
            var coll = _clientMongoDb.GetCollection<Account>(typeof(Account).Name);
            var q = coll.AsQueryable();
            if (status != null)
            {
                q = q.Where(a => a.Status == status.Value);
            }
            if (!string.IsNullOrWhiteSpace(searchText))
            {
                q = q.Where(a => a.Name.ToUpperInvariant().Contains(searchText.ToUpperInvariant()));
            }

            if (tier1_or_tier2)
            {
                q = q.Where(a => a.PlatformTier == AccessLevel.Tier1 || a.PlatformTier == AccessLevel.Tier2);
            }

            return q.Count();
        }

        public Account GetAccountById(Guid id)
        {
            var account = TryGetAccountById(id);
            if (account == null)
                throw new AccountNotFoundException(string.Format("Cannot find the account with id: '{0}'", id), id);

            return account;
        }

        public Account TryGetAccountById(Guid id)
        {
            return _accountCollection.Find(cu => cu.Id == id).FirstOrDefault();
        }

        public List<Guid> ListAccountsIds()
        {
            return _accountCollection.Find(x => true).Project(a => a.Id).ToListAsync().Result;
        }

        public void DeactivateAccounts(List<Guid> accountsIds, DateTime currentSyncDate)
        {
            if (accountsIds == null)
            {
                throw new ArgumentException("No account IDs provided for deactivation!");
            }

            var query = Builders<Account>.Filter.Where(a => accountsIds.Contains(a.Id) && a.Status != Status.Inactive);
            _clientMongoDb.GetCollection<Account>(typeof(Account).Name).UpdateMany(
                query,
                Builders<Account>.Update
                    .Set(a => a.Status, Status.Inactive)
                    .Set(a => a.LastCrmSync, currentSyncDate));
        }

        public void SaveAccount(Account account)
        {
            if (account == null)
            {
                throw new ArgumentException("No account provided for saving!");
            }

            var dbAccounts = _clientMongoDb.GetCollection<Account>(typeof(Account).Name);

            dbAccounts.FindOneAndReplace(
                Builders<Account>.Filter.Eq(x=> x.Id, account.Id),
                account,
                new FindOneAndReplaceOptions<Account> { IsUpsert = true });
        }

        public void SaveAccounts(List<Account> accounts)
        {
            if (accounts == null)
            {
                throw new ArgumentException("No accounts provided for saving!");
            }

            var dbAccounts = _clientMongoDb.GetCollection<Account>(typeof(Account).Name);

            accounts.ForEach(a => dbAccounts.FindOneAndReplace(
                Builders<Account>.Filter.Eq(x => x.Id, a.Id), a, new FindOneAndReplaceOptions<Account> { IsUpsert = true }));
        }

        public void SaveUsers(List<User> users)//used for testing, todo:insert ClientUsers too?
        {
            if (users == null)
            {
                throw new ArgumentException("No users provided for saving!");
            }

            var dbUsers = _clientMongoDb.GetCollection<User>(typeof(User).Name);

            users.ForEach(a => dbUsers.FindOneAndReplace(
                Builders<User>.Filter.Eq(x => x.Id, a.Id), a, new FindOneAndReplaceOptions<User> { IsUpsert = true }));
        }

        public void SaveClientUsers(List<ClientUser> clientUsers)
        {
            if (clientUsers == null)
            {
                throw new ArgumentException("No client users provided for saving!");
            }

            var dbClientUsers = _clientMongoDb.GetCollection<ClientUser>(typeof(ClientUser).Name);

            clientUsers.ForEach(a => dbClientUsers.FindOneAndReplace(
                Builders<ClientUser>.Filter.Eq(x => x.Id, a.Id), a, new FindOneAndReplaceOptions<ClientUser> { IsUpsert = true }));
        }

        public int GetUsersByAccountIdTotalCount(Status? status, Guid accountId)
        {
            var coll = _clientMongoDb.GetCollection<User>(typeof(User).Name);
            if (status == null)
            {
                return coll.AsQueryable().Count(a => a.AccountId == accountId);
            }
            return coll.AsQueryable().Count(a => a.AccountId == accountId && a.Status == status.Value);
        }

        public void SavePasswordByUsername(string username, string password, DateTime expirationDate)
        {
            var userColl = _clientMongoDb.GetCollection<User>(typeof(User).Name);
            var clientUserColl = _clientMongoDb.GetCollection<ClientUser>(typeof(ClientUser).Name);

            var user = userColl.AsQueryable().SingleOrDefault(u => u.Email.ToLowerInvariant() == username.ToLowerInvariant());

            if (user == null)
                throw new ArgumentException(string.Format("Cannot find the user with email address: {0}", username));

            var clientUser = clientUserColl.AsQueryable().SingleOrDefault(cu => cu.Id == user.Id);
            if (clientUser == null)
                throw new ArgumentException(string.Format("Cannot find the client user with id: {0}", user.Id));

            clientUser.Password = password;
            clientUser.PasswordExpirationDate = expirationDate;
            clientUserColl.FindOneAndReplace(Builders<ClientUser>.Filter.Eq(x => x.Id, clientUser.Id), clientUser, new FindOneAndReplaceOptions<ClientUser> { IsUpsert = true });
        }

        public void SavePasswordByUserId(Guid userId, string password, DateTime expirationDate)
        {
            var clientUserColl = _clientMongoDb.GetCollection<ClientUser>(typeof(ClientUser).Name);

            var clientUser = clientUserColl.AsQueryable().SingleOrDefault(cu => cu.Id == userId);
            if (clientUser == null)
                throw new ArgumentException(string.Format("Cannot find the client user with id: {0}", userId));

            clientUser.Password = password;
            clientUser.PasswordExpirationDate = expirationDate;
            clientUserColl.FindOneAndReplace(Builders<ClientUser>.Filter.Eq(x => x.Id, clientUser.Id), clientUser, new FindOneAndReplaceOptions<ClientUser> { IsUpsert = true });
        }

        public User GetUserByUserId(Guid userId)
        {
            var user = TryGetUserByUserId(userId);
            if (user == null)
                throw new UserNotFoundException(string.Format("Cannot find the user with id: '{0}'", userId), userId);

            return user;
        }

        public User TryGetUserByUserId(Guid userId)
        {
            return _clientMongoDb.GetCollection<User>(typeof(User).Name).AsQueryable()
                .SingleOrDefault(cu => cu.Id == userId);
        }

        public bool VerifyUserExists(Guid userId)
        {
            return _clientMongoDb.GetCollection<User>(typeof(User).Name).AsQueryable()
                .Any(cu => cu.Id == userId);
        }

        public User TryGetUserByUsername(string username)
        {
            return _clientMongoDb.GetCollection<User>(typeof(User).Name).AsQueryable()
                .SingleOrDefault(cu => cu.Email.ToLowerInvariant() == username.ToLowerInvariant());
        }

        public User GetUserByUsername(string username)
        {
            var user = TryGetUserByUsername(username);
            if (user == null)
                throw new ArgumentNullException(string.Format("Cannot find the user with username: {0}", username));

            return user;
        }

        public void UpdateAccountNameForUsers(Account account)
        {
            if (account == null)
            {
                throw new ArgumentException("No account provided for updating its users!");
            }

            var query = Builders<User>.Filter.Where(u => u.AccountId == account.Id);
            _clientMongoDb.GetCollection<User>(typeof(User).Name).UpdateMany(
                query,
                Builders<User>.Update.Set(u => u.Account, account.Name));
        }

        public void UpdateAccountNameForFakeIPUsers(Account account)
        {
            if (account == null)
            {
                throw new ArgumentException("No account provided for updating its fake users!");
            }

            var query = Builders<User>.Filter.Where(u => u.AccountId == account.Id && u.IsFakeIpUser);
            _clientMongoDb.GetCollection<User>(typeof(User).Name).UpdateMany(
                query,
                Builders<User>.Update.Set(u => u.FirstName, account.Name));
        }

        public ClientUser GetClientUserByUserId(Guid userId)
        {
            var user = TryGetClientUserByUserId(userId);
            if (user == null)
                throw new ClientUserNotFoundException(string.Format("Cannot find the client user with id: '{0}'", userId), userId);

            return user;
        }

        public ClientUser TryGetClientUserByUserId(Guid userId)
        {
            return _clientMongoDb.GetCollection<ClientUser>(typeof(ClientUser).Name).AsQueryable().SingleOrDefault(cu => cu.Id == userId);
        }

        public ClientUser GetClientUserByUsername(string username)
        {
            var clientUserColl = _clientMongoDb.GetCollection<ClientUser>(typeof(ClientUser).Name);
            var userColl = _clientMongoDb.GetCollection<User>(typeof(User).Name);

            var user = userColl.AsQueryable().SingleOrDefault(cu => cu.Email.ToLowerInvariant() == username.ToLowerInvariant());

            if (user == null)
                throw new ArgumentException(string.Format("Cannot find the user with email address: {0}", username));

            var clientUser = clientUserColl.AsQueryable().SingleOrDefault(cu => cu.Id == user.Id);
            if (clientUser == null)
                throw new ArgumentException(string.Format("Cannot find the client user with id: {0}", user.Id));

            return clientUser;
        }

        public DateTime GetLastSyncDate(SyncType syncType)
        {
            var lastSyncDate = _clientMongoDb.GetCollection<Synchronization>(typeof(Synchronization).Name)
                                    .AsQueryable()
                                    .Where(s => s.Id == syncType)
                                    .Select(s => s.LastSuccessfulSync)
                                    .SingleOrDefault();

            return lastSyncDate > _crmMinDate ? lastSyncDate : _crmMinDate;
        }

        public void SaveLastSync(Synchronization lastSync)
        {
            if (lastSync == null)
            {
                throw new ArgumentException("No synchronization data provided for saving!");
            }

            _clientMongoDb.GetCollection<Synchronization>(typeof(Synchronization).Name)
                .FindOneAndReplace(Builders<Synchronization>.Filter.Eq(x=> x.Id, lastSync.Id), lastSync,
                new FindOneAndReplaceOptions<Synchronization> { IsUpsert = true });
        }

        public List<Guid> ListUsersIds(bool filterDeleted)
        {
            if (filterDeleted)
            {
                return _clientMongoDb.GetCollection<User>(typeof(User).Name).AsQueryable()
                    .Where(u => !u.Sync_IsDeleted)
                    .Select(a => a.Id)
                    .ToList();
            }

            ///todo  MMA check this
            return _clientMongoDb.GetCollection<User>(typeof(User).Name).AsQueryable()
                .Where(u => !u.Sync_IsDeleted)
                .Select(a => a.Id)
                .ToList();
        }

        public int DeleteUsers(List<Guid> userIds)
        {
            if (userIds == null)
            {
                throw new ArgumentException("No user IDs provided for deletion!");
            }

            if (userIds.Count == 0)
                return 0;

            var count = 0;
            userIds.ForEach(id =>
            {
                var user = GetUserByUserId(id);
                if (!user.IsFakeIpUser)
                {
                    user.Sync_IsDeleted = true;
                    user.Sync_DiscretStatus = Status.Inactive;
                    user.Status = Status.Inactive;
                    user.Sync_IsDirty = false;
                    SaveUser(user);
                    count++;
                }
            });
            return count;
        }

        public void SaveUser(User user)
        {
            if (user == null)
            {
                throw new ArgumentException("No user provided for saving!");
            }

            var dbUsers = _clientMongoDb.GetCollection<User>(typeof(User).Name);
            user.AdaptEmailToStatus(this);

            dbUsers.FindOneAndReplace(Builders<User>.Filter.Eq(x=> x.Id, user.Id),user, new FindOneAndReplaceOptions<User>() { IsUpsert = true });
        }

        public List<User> GetDirtyUsers(bool filterDeleted)
        {
            var coll = _clientMongoDb.GetCollection<User>(typeof(User).Name);

            if (filterDeleted)
            {
                return coll.AsQueryable()
                    .Where(a => a.Sync_IsDirty && a.Sync_IsDeleted == false)
                    .OrderBy(a => a.AccountId).ToList();
            }

            return coll.AsQueryable().Where(a => a.Sync_IsDirty).OrderBy(a => a.AccountId).ToList();
        }

        public List<Guid> GetAccountsByModifiedAfter(DateTime dateTime)
        {
            return _clientMongoDb.GetCollection<Account>(typeof(Account).Name).AsQueryable()
                .Where(a => a.LastCrmSync > dateTime).Select(a => a.Id).ToList();
        }

        public List<User> GetUsersByAccountId(Guid accountId, bool filterDeleted)
        {
            var coll = _clientMongoDb.GetCollection<User>(typeof(User).Name);
            if (filterDeleted)
            {
                return coll.AsQueryable().Where(a => a.AccountId == accountId && !a.Sync_IsDeleted).ToList();
            }

            return coll.AsQueryable().Where(a => a.AccountId == accountId).ToList();
        }

        public List<User> GetUsers(List<Guid> accountIds, Status? status, string searchText, ListOptions listOptions)
        {
            var coll = _clientMongoDb.GetCollection<User>(typeof(User).Name);
            var q = coll.AsQueryable();
            if (!string.IsNullOrEmpty(searchText))
            {
                q = q.Where(u => u.FullName.ToLowerInvariant().Contains(searchText) || u.Email.ToLowerInvariant().Contains(searchText) || u.Account.ToLowerInvariant().Contains(searchText));
            }
            if (accountIds != null && accountIds.Count > 0)
            {
                var nullableAccountIds = new List<Guid?>();
                foreach (var id in accountIds)
                {
                    nullableAccountIds.Add(id);
                }
                q = q.Where(u => nullableAccountIds.Contains(u.AccountId));
            }
            if (status != null)
            {
                q = q.Where(u => u.Status == status.Value);
            }

            if (listOptions != null && listOptions.OrderBy != null && listOptions.OrderBy.Any())
            {
                foreach (var sortOption in listOptions.OrderBy)
                {
                    var accountsSortBy = (UsersSortEnum)Enum.Parse(typeof(UsersSortEnum), sortOption, true);

                    switch (accountsSortBy)
                    {
                        case UsersSortEnum.FullName:
                            q = listOptions.OrderAscending ? q.OrderBy(u => u.FullName) : q.OrderByDescending(u => u.FullName);
                            break;

                        case UsersSortEnum.Email:
                            q = listOptions.OrderAscending ? q.OrderBy(u => u.Email) : q.OrderByDescending(u => u.Email);
                            break;

                        case UsersSortEnum.Account:
                            q = listOptions.OrderAscending ? q.OrderBy(u => u.Account) : q.OrderByDescending(u => u.Account);
                            break;

                        case UsersSortEnum.LastAccessDate:
                            q = listOptions.OrderAscending ? q.OrderBy(u => u.ModifiedOn) : q.OrderByDescending(u => u.ModifiedOn);    // the field LastAccessDate is not implemented yet
                            break;

                        case UsersSortEnum.Status:
                            q = listOptions.OrderAscending ? q.OrderBy(u => u.Status) : q.OrderByDescending(u => u.Status);
                            break;

                        default:
                            q = q.OrderBy(u => u.FullName);
                            break;
                    }
                }
            }

            return q.Skip(listOptions.Skip).Take(listOptions.Take).ToList();
        }

        public int GetUsersTotalCount(List<Guid> accountIds, Status? status, string searchText)
        {
            var coll = _clientMongoDb.GetCollection<User>(typeof(User).Name).AsQueryable();

            if (status != null)
            {
                coll = coll.Where(u => u.Status == status.Value);
            }

            if (!string.IsNullOrEmpty(searchText))
            {
                coll = coll.Where(u => u.FullName.ToLowerInvariant().Contains(searchText) || u.Email.ToLowerInvariant().Contains(searchText) || u.Account.ToLowerInvariant().Contains(searchText));
            }

            if (accountIds != null && accountIds.Count > 0)
            {
                var nullableAccountIds = new List<Guid?>();
                foreach (var id in accountIds)
                {
                    nullableAccountIds.Add(id);
                }
                coll = coll.Where(u => nullableAccountIds.Contains(u.AccountId));
            }

            return coll.Count();
        }

        public List<Account> GetAccountsByUserSatusAndSearchFilter(Status? userStatus, string filterText)
        {
            var coll = _clientMongoDb.GetCollection<User>(typeof(User).Name);
            var q = coll.AsQueryable();

            if (userStatus != null)
            {
                q = q.Where(u => u.Status == userStatus.Value);
            }

            if (!string.IsNullOrEmpty(filterText))
            {
                q = q.Where(u => u.FullName.ToLowerInvariant().Contains(filterText) || u.Email.ToLowerInvariant().Contains(filterText) || u.Account.ToLowerInvariant().Contains(filterText));
            }

            var uersList = q.OrderBy(u => u.Account).ToList();
            var accounts = new List<Account>();

            if (uersList != null && uersList.Count > 0)
            {
                foreach (User user in uersList)
                {
                    if (!accounts.Any(a => a.Id == user.AccountId) && user.AccountId.HasValue)
                    {
                        accounts.Add(new Account { Id = user.AccountId.Value, Name = user.Account });
                    }
                }
            }

            return accounts;
        }

        #region reactivated accounts events

        public List<ReactivatedSyncEvent> GetReactivatedSyncEvents(int skip, int take)
        {
            return _clientMongoDb.GetCollectionByNameType<ReactivatedSyncEvent>().AsQueryable().Skip(skip).Take(take).ToList();
        }

        public void SaveReactivatedSyncEvents(List<ReactivatedSyncEvent> syncEvents)
        {
            if (syncEvents == null || syncEvents.Count == 0)
            {
                return;
            }

            var collection = _clientMongoDb.GetCollectionByNameType<ReactivatedSyncEvent>();

            syncEvents.ForEach(a => collection.FindOneAndReplace(
                Builders<ReactivatedSyncEvent>.Filter.Eq(x=> x.Id, a.Id), a,
                new FindOneAndReplaceOptions<ReactivatedSyncEvent> { IsUpsert = true }));
        }

        public void DeleteReactivatedSyncEvent(Guid id)
        {
            var collection = _clientMongoDb.GetCollectionByNameType<ReactivatedSyncEvent>();
            collection.DeleteMany(Builders<ReactivatedSyncEvent>.Filter.Where(c => c.Id == id));
        }

        public List<Guid> ListAccountsByIdAndStatus(List<Guid> list, Status status)
        {
            var collection = _clientMongoDb.GetCollectionByNameType<Account>();

            return collection.Find(Builders<Account>.Filter.And(
                Builders<Account>.Filter.In(x => x.Id, list),
                Builders<Account>.Filter.Eq(x => x.Status, status)))
                .Project(x => x.Id)
                .ToList();
        }

        public List<Account> GeAccountsByUpcomingEsgProfiles()
        {
            return _clientMongoDb.GetCollectionByNameType<Account>().AsQueryable().Where(a => a.UpcomingEsgProfile != null).ToList();
        }

        public void UpdateCurrentEsgProfile(Account profile)
        {
            var collection = _clientMongoDb.GetCollection<Account>(typeof(Account).Name);
            if (!profile.UpcomingEsgProfile.HasValue)
                throw new ArgumentException(string.Format("The account {0} does not have an upcoming esg profile",
                    profile.Id));
            if (profile.UpcomingEsgProfile.Value == Guid.Empty)
            {
                throw new ArgumentException(string.Format("The account {0} does not have an upcoming esg profile is a guid empty",
                    profile.Id));
            }
            profile.CurrentEsgProfile = profile.UpcomingEsgProfile.Value;
            profile.UpcomingEsgProfile = null;
            profile.ModifiedOn = DateTime.Now;
            collection.FindOneAndReplace(
                Builders<Account>.Filter.Eq(x=> x.Id, profile.Id),
                profile,
                new FindOneAndReplaceOptions<Account> { IsUpsert = true });
        }

        public List<Account> GetAccountsByds(List<Guid> ids = null)
        {
            List<Guid> data;
            data = ids ?? new List<Guid>();
            var coll = _clientMongoDb.GetCollection<Account>(typeof(Account).Name);
            var q = coll.AsQueryable();
            if (!data.Any())
                return q.ToList();
            return q.Where(a => data.Contains(a.Id)).ToList();
        }

        #endregion

        #region Invalid Sync Users

        private const string _invalidUserCollectionName = "InvalidSyncUsers";

        public List<Guid> GetInvalidSyncUsers(InvalidSynUserType type)
        {
            var coll = _clientMongoDb.GetCollection<User>(_invalidUserCollectionName);
            switch (type)
            {
                case InvalidSynUserType.NullEmail:
                    return coll.AsQueryable().Where(u => u.Email == null).Select(u => u.Id).ToList();

                case InvalidSynUserType.All:
                    return coll.AsQueryable().Select(u => u.Id).ToList();

                default:
                    return coll.AsQueryable().Where(u => u.Email != null).Select(u => u.Id).ToList();
            }
        }

        public void DeleteInvalidSyncUsers(List<Guid> users)
        {
            if (users == null)
            {
                throw new ArgumentException("No invalid users provided for deletion!");
            }

            if (users.Count == 0)
                return;

            _clientMongoDb.GetCollection<User>(_invalidUserCollectionName).DeleteMany(
                Builders<User>.Filter.In(a => a.Id, users));
        }

        public void SaveInvalidSyncUsers(List<User> users)
        {
            if (users == null)
            {
                throw new ArgumentException("No invalid users provided for saving!");
            }

            if (users.Count == 0)
                return;

            var coll = _clientMongoDb.GetCollection<User>(_invalidUserCollectionName);

            users.ForEach(a => coll.FindOneAndReplace(
                Builders<User>.Filter.Eq(x=> x.Id, a.Id), a,
                new FindOneAndReplaceOptions<User> { IsUpsert = true }));
        }

        #endregion
    }
}
